/**********************************************************
 * Copyright © 2024 Walkline Wang (https://walkline.wang)
 * Gitee: https://gitee.com/walkline/keypad-for-ops
 **********************************************************/
#ifndef __WS2812__
#define __WS2812__

#include "src/common.h"
#include "src/hal/hal_def.h"
#include <SPI.h>

constexpr int8_t LEDS_ALL = -1;

constexpr uint8_t WS2812_RESET_PULSE = 64;
constexpr uint16_t DMA_BUFFER_SIZE = LEDS_COUNT * 24;

// https://zhuanlan.zhihu.com/p/651762507
#define FILL_DMA_BUFFER(COLOR)                                                 \
    for (uint8_t mask = 0x80; mask; mask >>= 1) {                              \
        if (COLOR & mask) {                                                    \
            *ptr++ = 0xfc;                                                     \
        } else {                                                               \
            *ptr++ = 0x80;                                                     \
        }                                                                      \
    }

extern SPI_HandleTypeDef spi2_handle;

class WS2812 {
  public:
    struct ws2812_color_t {
        uint8_t red;
        uint8_t green;
        uint8_t blue;

        ws2812_color_t() : red(0), green(0), blue(0){};

        ws2812_color_t(uint8_t red, uint8_t green, uint8_t blue)
            : red(red), green(green), blue(blue){};

        ws2812_color_t(uint8_t *color)
            : red(color[0]), green(color[1]), blue(color[2]){};

        void brightness(float bright_percent) {
            red = uint8_t(red * bright_percent);
            green = uint8_t(green * bright_percent);
            blue = uint8_t(blue * bright_percent);

            red = constrain(red, 0, 255);
            green = constrain(green, 0, 255);
            blue = constrain(blue, 0, 255);
        }
    };

  private:
    uint8_t dma_buffer[DMA_BUFFER_SIZE];

    float bright_percent = 1.0f;

  public:
    /* 初始化 SPI 外设 */
    void setup(void);

    void brightness(float value);

    /* 显示颜色数据 */
    void set_color(int index, ws2812_color_t color);

    /* 清除颜色数据 */
    void clean(int index = LEDS_ALL);

  private:
    /* 重新初始化 SPI 并启用 DMA 功能 */
    void init_spi_with_dma(void);

    void spi_transmit_dma(void);
};

#endif
